

import time
import cPickle
import unittest

from sqlalchemy.exc import IntegrityError

#from dexen.system.client import rpc_api
#from dexen.user.libs import constants, server_api

from dexen.system.client import frontend

class TestHelpers(unittest.TestCase):

    def __init__(self):
        self.url = "localhost"
        
    def setUp(self):
        frontend.connect_to_server(self.url)
    
    def tearDown(self):
        pass
        
    def createSchemas(self, n=1):
        """Create n schemas.
        
        A helper method that creates n schemas with names schema1, schema2 and 
        so on.
        
        Arguments:
        n: the number of schemas (default 1)
        
        """
        
        for i in range(n):
            self.frontend_api.set_schema("schema%s"%(i+1), 
                                        cPickle.dumps(range(i), 
                                                      cPickle.HIGHEST_PROTOCOL), 
                                        time.time())
            
    def createPops(self, schema_name, n=1):
        """Create n populations under schema_name.
        
        A helper method that creates n populations under schema_name
        with names pop1, pop2 and so on.
        
        Arguments:
        schema_name: the name of the schema
        n: the number of populations (default 1)
        
        Note:
        Make sure to create a schema with the name 'schema_name' 
        before calling this function.
        
        """
        
        for i in range(n):
            self.frontend_api.set_population(schema_name, "pop%s"%(i+1),
                                            time.time())

    def createScripts(self, schema_name, pop_name, n=1):
        """Create n scripts.
        
        Creates n scripts with names script1, script2 and so on.
        
        Note: In order for the script to be bound to a population,
        the schema_name and pop_name has to be set in config object through
        the setRPYCObj method.
        
        """
        
        self.configs = []
        
        for i in range(n):
            script_name = "script%s"%(i+1)
            config_api = server_api.ConfigAPI(self.url, schema_name, pop_name, 
                                              script_name)
            
            config_api.setScript(script_name, "fileA", "funcA", 
                                 constants.LOOPED, ("genotype",),
                                 exclusive_use=True)
    
    def createDataStructure(self):
        """Create data structure.
        
        Note:
        The attributes dictionary has to be pickled before passed as argument.
        This is due to rpyc limitation. The data structure is linked to 
        population therefore the schema_name and pop_name has to be set in the 
        config object in advance by the setRPYC method.
        
        """
        
        attributes = {
            "genotype" : constants.BLOB,
            "phenotype" : constants.BLOB,
            "eval1" : constants.FLOAT,
            "eval2" : constants.FLOAT
        }        
        self.config.exposed_setDataStructure(cPickle.dumps(attributes, 
                                             cPickle.HIGHEST_PROTOCOL))
                
class ConfigServiceTests(TestHelpers):
    """ Contains test cases for ConfigService class."""
    
    def setUp(self):
        server.initialize_databases()
        TestHelpers.setUp(self)
        
    def test_setDataStructure(self):
        self.createSchemas(1)
        self.createPops("schema1", 5)
        self.config.exposed_setRPYCObj("schema1", "pop1")
        self.createDataStructure()
        self.createScripts(4)
        print self.frontend.exposed_get_script_names("schema1", "pop1")
        
class LooperServiceTests(TestHelpers):
    pass

class FrontendServiceTests(TestHelpers):
    def setUp(self):
        server.initialize_databases()
        TestHelpers.setUp(self)
                
    def test_duplicateSchemaNames(self):
        """Test for duplicate schema names.
        
        Creating two schemas with the same name in the database is supposed to
        raise an integrity exception.
        
        """
        
        self.createSchemas(3)
        self.assertRaises(IntegrityError, self.frontend.exposed_set_schema, 
                          "schema3", "fdafd", time.time())
    
    def test_duplicatePopNames(self):
        """Test for duplicate population names under the same schema.
        
        A schema 'schema1' is created and two populations with identical
        names 'pop1' are created under 'schema1'.
         
        """
        
        self.createSchemas(1)
        self.createPops("schema1", 2)
        self.assertRaises(IntegrityError, self.frontend.exposed_set_population, 
                          "schema1", "pop1", time.time())

    def test_duplicatePopNames_2(self):
        """Test for duplicate population names under different schemas.
        
        Two schemas are created and two populations with identical
        names 'pop1' are created under 'schema1' and 'schema2'
        respectively.
        
        There is supposed to be no exception.
         
        """
        
        try:
            self.createSchemas(2)
            self.createPops("schema1", 1)
            self.createPops("schema2", 1)
        except:
            self.fail("")
        
        
        
def initialize():
    """Initialize all the databases on the server."""
    #server.initialize_databases()

if __name__ == "__main__":
    #initialize()
    unittest.main()

